/*
 * sockets-win32.c
 *
 *  Created on: 2012-05-20
 *      Author: Aco
 */

#include "sockets.h"
#include "threading.h"

#ifdef WIN32

#include <winsock2.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define CUS_WIN_WINSOCK_INIT_FAIL (1)
#define CUS_WIN_SOCKET_CREATE_FAIL (2)
#define CUS_WIN_BIND_FAIL (3)
#define CUS_WIN_LISTEN_FAIL (4)
#define CUS_WIN_CONNECT_FAIL (5)
#define CUS_WIN_ACCEPT_FAIL (6)
#define CUS_WIN_INVALID_ADDRESS (7)

int __sockError = 0;
int __winSockInit = 0;

CUSocket * cu_createTCPSocket()
{
	int sock;

	if(!__winSockInit)
	{
		WSADATA wsaData;

		if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0)
		{
			__sockError = CUS_WIN_WINSOCK_INIT_FAIL;
			puts("WSAStartup() failed"); fflush(stdout);
			return NULL;
		}
		else
		{
			__winSockInit = 1;
		}
	}

	if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
	{
		__sockError = CUS_WIN_SOCKET_CREATE_FAIL;
		printf("socket() failed: %d\n", WSAGetLastError()); fflush(stdout);
		return NULL;
	}
	else
	{
		CUSocket * cusock = (CUSocket*)malloc(sizeof(CUSocket));
		cusock->win_socket = sock;
		cusock->protocol = CU_TCP;
		return cusock;
	}
}

CUSocket * cu_createUDPSocket(int port)
{
	int sock;

	if(!__winSockInit)
	{
		WSADATA wsaData;

		if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0)
		{
			__sockError = CUS_WIN_WINSOCK_INIT_FAIL;
			puts("WSAStartup() failed"); fflush(stdout);
			return NULL;
		}
		else
		{
			__winSockInit = 1;
		}
	}

	if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
	{
		__sockError = CUS_WIN_SOCKET_CREATE_FAIL;
		printf("socket() failed: %d\n", WSAGetLastError()); fflush(stdout);
		return NULL;
	}
	else
	{
		CUSocket * cusock = (CUSocket*)malloc(sizeof(CUSocket));

		memset(&cusock->hostAddr, 0, sizeof(cusock->hostAddr));
		cusock->hostAddr.sin_family = AF_INET;
		cusock->hostAddr.sin_addr.s_addr = htonl(INADDR_ANY);
		cusock->hostAddr.sin_port = htons(port);

		if (bind(sock, (struct sockaddr *) &(cusock->hostAddr), sizeof(cusock->hostAddr)) < 0)
		{
			__sockError = CUS_WIN_BIND_FAIL;
			free(cusock);
			return NULL;
		}

		cusock->win_socket = sock;
		cusock->port = port;
		cusock->protocol = CU_UDP;
		return cusock;
	}
}

int cu_checkIPaddress(const char * address)
{
	int p1 = 256, p2 = 256, p3 = 256, p4 = 256;
	if(sscanf(address, "%d.%d.%d.%d", &p1, &p2, &p3, &p4) != 4)
	{
		__sockError = CUS_WIN_INVALID_ADDRESS;
		return CU_ERROR;
	}
	else
	{
		if(p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 ||
				p3 < 0 || p3 > 255 || p4 < 0 || p4 > 255)
		{
			__sockError = CUS_WIN_INVALID_ADDRESS;
			return CU_ERROR;
		}
	}
	return CU_OK;
}

int cu_openServerSocket(CUSocket * sock, int port)
{
	struct sockaddr_in echoServAddr;

	memset(&echoServAddr, 0, sizeof(echoServAddr));
	echoServAddr.sin_family = AF_INET;
	echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	echoServAddr.sin_port = htons(port);

	if (bind(sock->win_socket, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
	{
		__sockError = CUS_WIN_BIND_FAIL;
		return CU_ERROR;
	}

	if (listen(sock->win_socket, MAX_PENDING) < 0)
	{
		__sockError = CUS_WIN_LISTEN_FAIL;
		return CU_ERROR;
	}

	sock->ipAddr = 0x7F000001;
	strcpy(sock->ipTxt, "127.0.0.1");
	sock->port = port;

	return CU_OK;
}

int cu_openClientSocket(CUSocket * sock, const char * address, int port)
{
	struct sockaddr_in servAddr;

	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family      = AF_INET;
	servAddr.sin_addr.s_addr = inet_addr(address);
	servAddr.sin_port        = htons(port);

	if (connect(sock->win_socket, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0)
	{
		__sockError = CUS_WIN_CONNECT_FAIL;
		return CU_ERROR;
	}

	sock->ipAddr = 0x7F000001;
	strcpy(sock->ipTxt, "127.0.0.1");
	sock->port = port;

	return CU_OK;
}

CUSocket * cu_acceptConnection(CUSocket * sock)
{
	int csock = -1;
	struct sockaddr_in client_info = {0};
	int size = sizeof(client_info);

	if((csock = accept(sock->win_socket, (struct sockaddr*)&client_info, &size)) < 0)
	{
		__sockError = CUS_WIN_ACCEPT_FAIL;
		return NULL;
	}
	else
	{
		CUSocket * cusock = (CUSocket*)malloc(sizeof(CUSocket));

		cusock->win_socket = csock;
		cusock->ipAddr = client_info.sin_addr.S_un.S_addr;
		cusock->port = client_info.sin_port;
		sprintf(cusock->ipTxt, "%d.%d.%d.%d",
				client_info.sin_addr.S_un.S_un_b.s_b1,
				client_info.sin_addr.S_un.S_un_b.s_b2,
				client_info.sin_addr.S_un.S_un_b.s_b3,
				client_info.sin_addr.S_un.S_un_b.s_b4);

		return cusock;
	}
}

int cu_closeSocket(CUSocket * sock)
{
	if(sock == NULL) return CU_ERROR;
	closesocket(sock->win_socket);
	return CU_OK;
}

int cu_sendData(CUSocket * sock, const char * buffer, int length)
{
	if(sock == NULL) return CU_ERROR;
	if(length == 0) length = strlen(buffer) + 1;
	if(sock->protocol == CU_TCP)
		return send(sock->win_socket, buffer, length, 0);
	else
		return -1;
}

int cu_sendDataToIP(CUSocket * sock, const char * buffer, int length, unsigned int ip, int port)
{
	if(sock == NULL) return CU_ERROR;
	if(length == 0) length = strlen(buffer) + 1;
	if(sock->protocol == CU_UDP)
	{
		struct sockaddr_in dest;
		dest.sin_family = AF_INET;
		dest.sin_addr.s_addr = ip;
		dest.sin_port = htons(port);
		return sendto(sock->win_socket, buffer, length, 0, (const struct sockaddr *) &dest, sizeof(dest));
	}
	else
	{
		return -1;
	}
}

int cu_sendDataTo(CUSocket * sock, const char * buffer, int length, const char * ip, int port)
{
	return cu_sendDataToIP(sock, buffer, length, inet_addr(ip), port);
}

int cu_recvData(CUSocket * sock, char * buffer, int length)
{
	if(sock == NULL) return CU_ERROR;
	return recv(sock->win_socket, buffer, length, 0);
}

int cu_recvSpin(CUSocket * sock, char * buffer, int length, int cycle, int count)
{
	int i = 0;
	if(sock == NULL) return CU_ERROR;
	for(i = 0; i < count; ++i)
	{
		cu_sleep(cycle);
		if(recv(sock->win_socket, buffer, length, MSG_PEEK) > 0)
		{
			return recv(sock->win_socket, buffer, length, 0);
		}
	}
	return CU_ERROR;
}

int cu_recvDataFrom(CUSocket * sock, char * buffer, int length, unsigned int * ip, char * ipTxt, int * port)
{
	int result = 0;
	struct sockaddr_in SenderAddr;
	int SenderAddrSize = sizeof(SenderAddr);

	if(sock == NULL) return CU_ERROR;
	if(sock->protocol != CU_UDP) return CU_ERROR;

	result = recvfrom(sock->win_socket,
	                       buffer, length, 0, (SOCKADDR *) & SenderAddr, &SenderAddrSize);

	if(result >= 0)
	{
		if(ip != NULL) *ip = SenderAddr.sin_addr.S_un.S_addr;
		if(port != NULL) *port = ntohs(SenderAddr.sin_port);
		if(ipTxt != NULL)
		{
			sprintf(ipTxt, "%d.%d.%d.%d",
					SenderAddr.sin_addr.S_un.S_un_b.s_b1,
					SenderAddr.sin_addr.S_un.S_un_b.s_b2,
					SenderAddr.sin_addr.S_un.S_un_b.s_b3,
					SenderAddr.sin_addr.S_un.S_un_b.s_b4);
		}
	}
	else
	{
		printf("Error : %d\n", WSAGetLastError());
	}

	return result;
}

const char * cu_getSocketError()
{
	switch(__sockError)
	{
	case 0:
		return "No error"; break;
	default:
		return "Unknown error"; break;
	}
	return "";
}

void cu_clearSocketError()
{
	__sockError = 0;
}

#endif


